using rcSizeType = System.Int64;

namespace Recast
{
	public unsafe partial class RecastGlobal
	{
		public unsafe partial class rcVectorBase<T> where T : struct
		{
			public rcSizeType m_size;
			public rcSizeType m_cap;
			public T* m_data;

			public rcVectorBase() /*: m_size(0), m_cap(0), m_data(0)*/
			{
			}
			public rcVectorBase(rcVectorBase<T> other) /*: m_size(0), m_cap(0), m_data(0) */
			{
				assign(other.begin(), other.end());
			}
			public rcVectorBase(rcSizeType count) /*: m_size(0), m_cap(0), m_data(0)*/
			{
				resize(count);
			}
			public rcVectorBase(rcSizeType count, T* value) /*: m_size(0), m_cap(0), m_data(0)*/
			{
				resize(count, value);
			}
			public rcVectorBase(T* begin, T* end) /*: m_size(0), m_cap(0), m_data(0)*/
			{
				assign(begin, end);
			}
			~rcVectorBase()
			{
				destroy_range(0, m_size);
				rcFree(m_data);
			}

			public void clear()
			{
				destroy_range(0, m_size);
				m_size = 0;
			}

			public rcSizeType size()
			{
				return m_size;
			}
			public rcSizeType capacity()
			{
				return m_cap;
			}
			public bool empty()
			{
				return size() == 0;
			}

			public ref T front()
			{
				rcAssert(m_size);
				return ref m_data[0];
			}
			public ref T back()
			{
				rcAssert(m_size);
				return ref m_data[m_size - 1];
			}

			public T* data()
			{
				return m_data;
			}

			public T* begin()
			{
				return m_data;
			}
			public T* end()
			{
				return m_data + m_size;
			}

			public void resize(rcSizeType size)
			{
				resize_impl(size, null);
			}
			public void resize(rcSizeType size, T* value)
			{
				resize_impl(size, value);
			}

			public bool reserve(rcSizeType count)
			{
				if (count <= m_cap)
				{
					return true;
				}
				T* new_data = allocate_and_copy(count);
				if (null == new_data)
				{
					return false;
				}
				destroy_range(0, m_size);
				rcFree(m_data);
				m_data = new_data;
				m_cap = count;
				return true;
			}
			public T* allocate_and_copy(rcSizeType size)
			{
				//rcAssert(RC_SIZE_MAX / static_cast<rcSizeType>(sizeof(T)) >= size);
				T* new_data = (T*)rcAlloc((int)(sizeof(T) * size), rcAllocHint.RC_ALLOC_PERM);
				if (null != new_data)
				{
					copy_range(new_data, m_data, m_data + m_size);
				}
				return new_data;
			}
			public void assign(T* begin, T* end)
			{
				clear();
				reserve(end - begin);
				m_size = end - begin;
				copy_range(m_data, begin, end);
			}
			public void push_back(T value)
			{
				if (m_size < m_cap)
				{
					construct(m_data + m_size++, value);
					return;
				}
				rcSizeType new_cap = get_new_capacity(m_cap + 1);
				T* data = allocate_and_copy(new_cap);
				construct(data + m_size, value);
				destroy_range(0, m_size);
				m_size++;
				m_cap = new_cap;
				rcFree(m_data);
				m_data = data;
			}
			public void pop_back()
			{
				rcAssert(m_size > 0);
				back();//.~T();
				m_size--;
			}
			public rcSizeType get_new_capacity(rcSizeType min_capacity)
			{
				rcAssert(min_capacity <= RC_SIZE_MAX);
				if ((m_cap >= RC_SIZE_MAX / 2))
					return RC_SIZE_MAX;
				return 2 * m_cap > min_capacity ? 2 * m_cap : min_capacity;
			}
			public void resize_impl(rcSizeType size, T* value)
			{
				if (size < m_size)
				{
					destroy_range(size, m_size);
					m_size = size;
				}
				else if (size > m_size)
				{
					if (size <= m_cap)
					{
						if (null != value)
						{
							ruct_range(m_data + m_size, m_data + size, ref *value);
						}
						else
						{
							ruct_range(m_data + m_size, m_data + size);
						}
						m_size = size;
					}
					else
					{
						rcSizeType new_cap = get_new_capacity(size);
						T* new_data = allocate_and_copy(new_cap);
						if (null != value)
						{
							ruct_range(new_data + m_size, new_data + size, ref *value);
						}
						else
						{
							ruct_range(new_data + m_size, new_data + size);
						}
						destroy_range(0, m_size);
						rcFree(m_data);
						m_data = new_data;
						m_cap = new_cap;
						m_size = size;
					}
				}
			}
			public void swap(@rcVectorBase<T> other)
			{
				rcSizeType tmp_cap = other.m_cap;
				rcSizeType tmp_size = other.m_size;
				T* tmp_data = other.m_data;
				other.m_cap = m_cap;
				other.m_size = m_size;
				other.m_data = m_data;
				m_cap = tmp_cap;
				m_size = tmp_size;
				m_data = tmp_data;
			}
			static void construct(T* p, T v)
			{
				*p = v;
			}
			static void construct(T* p)
			{
				*p = default;
			}
			public void ruct_range(T* begin, T* end)
			{
				for (T* p = begin; p < end; p++)
				{
					construct(p);
				}
			}
			public void ruct_range(T* begin, T* end, ref T value)
			{
				for (T* p = begin; p < end; p++)
				{
					construct(p, value);
				}
			}
			public void copy_range(T* dst, T* begin, T* end)
			{
				for (rcSizeType i = 0; i < end - begin; i++)
				{
					construct(dst + i, begin[i]);
				}
			}
			public void destroy_range(rcSizeType begin, rcSizeType end)
			{
				for (rcSizeType i = begin; i < end; i++)
				{
					//m_data[i].~T();
				}
			}

			public ref T this[int index]
			{
				get
				{
					return ref m_data[index];
				}
			}
		}
	}
}
